package fun.tan90.device.protocol.config;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.StrUtil;
import fun.tan90.device.event.EventTopic;
import fun.tan90.device.event.model.DeviceSourceData;
import lombok.extern.slf4j.Slf4j;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.noear.dami.Dami;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.bean.LifecycleBean;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Component
public class ProtocolGraalFactory implements LifecycleBean {

    private static final String FUNC_KEY = "{}_{}";

    private final static Map<String, Value> FUNC_MAP = new HashMap<>();

    private final Context context = Context.newBuilder()
            .allowAllAccess(true)
            .option("engine.WarnInterpreterOnly", "false")
            .build();


    @Override
    public void start() {
        ONode protocolPyInfoNode = Dami.<Pair<String, String>, ONode>bus().sendAndRequest(EventTopic.PROTOCOL_GRAAL_INFO_TOPIC, null);
        log.info("ProtocolPyInfo: {}", protocolPyInfoNode.toJson());
        protocolPyInfoNode.ary().forEach(this::buildFunc);
    }

    @Override
    public void postStart() {
        Dami.<DeviceSourceData, ONode>bus().listen(EventTopic.PROTOCOL_GRAAL_EVAL_TOPIC, payload -> {
            DeviceSourceData deviceSourceData = payload.getContent();
            String deviceType = deviceSourceData.getDeviceType();
            String dataSource = deviceSourceData.getDataSource();
            Value value = FUNC_MAP.get(funcKey(deviceType, dataSource));
            if (Objects.isNull(value)) {
                payload.reply(null);
                return;
            }
            String data = deviceSourceData.getData();
            payload.reply(ONode.load(value.execute(data).as(Map.class)));
        });
    }

    private String funcKey(String deviceType, String dataSource) {
        return StrUtil.format(FUNC_KEY, deviceType, dataSource);
    }

    public void buildFunc(ONode protocolNode) {
        String deviceType = protocolNode.get("deviceType").getString();
        String dataSource = protocolNode.get("dataSource").getString();
        String language = protocolNode.get("language").getString();
        String codeSpec = protocolNode.get("codeSpec").getString();
        Source code = Source.create(language, codeSpec);
        context.eval(code);
        Value mainFunc = context.getBindings(language).getMember("main");
        FUNC_MAP.put(funcKey(deviceType, dataSource), mainFunc);
    }
}
